///
/// @file BinaryInstruction.cpp
/// @brief 二元操作指令
///
/// @author zenglj (zenglj@live.com)
/// @version 1.0
/// @date 2024-09-29
///
/// @copyright Copyright (c) 2024
///
/// @par 修改日志:
/// <table>
/// <tr><th>Date       <th>Version <th>Author  <th>Description
/// <tr><td>2024-09-29 <td>1.0     <td>zenglj  <td>新建
/// </table>
///
#include "BinaryInstruction.h"
#include "Function.h"
#include <GlobalVariable.h>
#include "IntegerType.h" // Ensure IntegerType is included

/// @brief 构造函数
/// @param _op 操作符
/// @param _result 结果操作数
/// @param _srcVal1 源操作数1
/// @param _srcVal2 源操作数2
BinaryInstruction::BinaryInstruction(Function * _func,
                                     IRInstOperator _op,
                                     Value * _srcVal1,
                                     Value * _srcVal2,
                                     Type * _type)
    : Instruction(_func, _op, _type)
{
    addOperand(_srcVal1);
    addOperand(_srcVal2);
}

/// @brief 转换成字符串
/// @param str 转换后的字符串
void BinaryInstruction::toString(std::string & str)
{
    Value *src1_val = getOperand(0);
    Value *src2_val = getOperand(1);
    Type* result_type = this->getType(); // Type of the instruction's result.
    str = ""; // Clear initial string

    // Lambda to process each operand (load + get name/type)
    auto process_operand_base = [&](Value* val, std::string& current_ir, Type*& effective_type) -> std::string {
        std::string name = val->getIRName();
        effective_type = val->getType();
        if (dynamic_cast<GlobalVariable*>(val) != nullptr || dynamic_cast<LocalVariable*>(val) != nullptr) {
            RegVariable* temp_var = func->newTempVariable(val->getType());
            name = temp_var->getIRName();
            current_ir += "\t" + name + " = load " + val->getType()->toString() + ", " + 
                          val->getType()->toString() + "* " + val->getIRName() + "\n";
            // effective_type remains val->getType() as it's the type of the loaded value
        }else if(val->getType()->isPointerType()){
            PointerType * ptype = static_cast<PointerType*>(val->getType());
            RegVariable* temp_var = func->newTempVariable(val->getType());
            name = temp_var->getIRName();
            current_ir += "\t" + name + " = load " + ptype->getPointeeType()->toString() + ", " + ptype->getPointeeType()->toString() + 
                          "* " + val->getIRName() + "\n";
        }
        return name;
    };

    std::string s1_loaded_name, s2_loaded_name;
    Type *s1_loaded_type, *s2_loaded_type;

    s1_loaded_name = process_operand_base(src1_val, str, s1_loaded_type);
    s2_loaded_name = process_operand_base(src2_val, str, s2_loaded_type);
    
    std::string s1_final_name = s1_loaded_name;
    std::string s2_final_name = s2_loaded_name;
    Type *s1_final_type = s1_loaded_type;
    Type *s2_final_type = s2_loaded_type;


    // Handle operations
    switch (op) {
        case IRInstOperator::IRINST_OP_ADD_I:
        case IRInstOperator::IRINST_OP_SUB_I:
        case IRInstOperator::IRINST_OP_MUL_I:
        case IRInstOperator::IRINST_OP_DIV_I:
        case IRInstOperator::IRINST_OP_MOD_I:
        {
            // Arithmetic ops. Operands and result are of `result_type` (e.g., i32).
            Type* op_domain_type = result_type; 
            std::string op_domain_type_str = op_domain_type->toString();

            if (s1_final_type != op_domain_type) {
                if (op_domain_type->isInt32Ty() && s1_final_type->isInt1Ty()) {
                    RegVariable* cast_var = func->newTempVariable(op_domain_type);
                    str += "\t" + cast_var->getIRName() + " = zext " + s1_final_type->toString() + " " + s1_final_name + " to " + op_domain_type_str + "\n";
                    s1_final_name = cast_var->getIRName();
                    s1_final_type = op_domain_type;
                } else { /* TODO: other casts if necessary, or error */ }
            }
            if (s2_final_type != op_domain_type) {
                if (op_domain_type->isInt32Ty() && s2_final_type->isInt1Ty()) {
                    RegVariable* cast_var = func->newTempVariable(op_domain_type);
                    str += "\t" + cast_var->getIRName() + " = zext " + s2_final_type->toString() + " " + s2_final_name + " to " + op_domain_type_str + "\n";
                    s2_final_name = cast_var->getIRName();
                    s2_final_type = op_domain_type;
                } else { /* TODO: other casts */ }
            }

            // std::string llvm_op_str;
            // if (op == IRInstOperator::IRINST_OP_ADD_I) llvm_op_str = "add nsw";
            // else if (op == IRInstOperator::IRINST_OP_SUB_I) llvm_op_str = "sub nsw";
            // else if (op == IRInstOperator::IRINST_OP_MUL_I) llvm_op_str = "mul nsw";
            // else if (op == IRInstOperator::IRINST_OP_DIV_I) llvm_op_str = "sdiv";
            // else if (op == IRInstOperator::IRINST_OP_MOD_I) llvm_op_str = "srem";
            // str += "\t" + getIRName() + " = " + llvm_op_str + " " + op_domain_type_str + " " + s1_final_name + ", " + s2_final_name;
            // 根据操作码和类型生成指令
            if (result_type->isFloatType()) {
                // 浮点数操作
                std::string llvm_op_str;
                if (op == IRInstOperator::IRINST_OP_ADD_I) llvm_op_str = "fadd";
                else if (op == IRInstOperator::IRINST_OP_SUB_I) llvm_op_str = "fsub";
                else if (op == IRInstOperator::IRINST_OP_MUL_I) llvm_op_str = "fmul";
                else if (op == IRInstOperator::IRINST_OP_DIV_I) llvm_op_str = "fdiv";
                str += "\t" + getIRName() + " = " + llvm_op_str + " " + result_type->toString() + " " + s1_final_name + ", " + s2_final_name;
            } else if (result_type->isIntegerType()) {
                // 整数操作
                std::string llvm_op_str;
                if (op == IRInstOperator::IRINST_OP_ADD_I) llvm_op_str = "add nsw";
                else if (op == IRInstOperator::IRINST_OP_SUB_I) llvm_op_str = "sub nsw";
                else if (op == IRInstOperator::IRINST_OP_MUL_I) llvm_op_str = "mul nsw";
                else if (op == IRInstOperator::IRINST_OP_DIV_I) llvm_op_str = "sdiv";
                else if (op == IRInstOperator::IRINST_OP_MOD_I) llvm_op_str = "srem";
                str += "\t" + getIRName() + " = " + llvm_op_str + " " + result_type->toString() + " " + s1_final_name + ", " + s2_final_name;
            } 
            else {
                // 其他类型处理
                Instruction::toString(str);
                if (!str.empty() && str.find('\n') == std::string::npos) {
                    str = "\t" + str;
                }
            }
            break;
        }

        case IRInstOperator::IRINST_OP_LESS_EQUAL_I:
        case IRInstOperator::IRINST_OP_GREATER_I:
        case IRInstOperator::IRINST_OP_GREATER_EQUAL_I:
        case IRInstOperator::IRINST_OP_LESS_I:
        case IRInstOperator::IRINST_OP_EQ_I:
        case IRInstOperator::IRINST_OP_NEQ_I:
        {
            // Comparison ops. Result is i1. Operands are compared using a common (e.g., i32) type.
            Type* cmp_type = IntegerType::getTypeInt(); // Assume i32 for int comparisons
            std::string cmp_type_str = cmp_type->toString();

            if (s1_final_type != cmp_type) {
                if (cmp_type->isInt32Ty() && s1_final_type->isInt1Ty()) { // i1 to i32 for comparison
                    RegVariable* cast_var = func->newTempVariable(cmp_type);
                    str += "\t" + cast_var->getIRName() + " = zext " + s1_final_type->toString() + " " + s1_final_name + " to " + cmp_type_str + "\n";
                    s1_final_name = cast_var->getIRName();
                    s1_final_type = cmp_type;
                } else { /* TODO: other casts */ }
            }
            if (s2_final_type != cmp_type) {
                if (cmp_type->isInt32Ty() && s2_final_type->isInt1Ty()) { // i1 to i32 for comparison
                    RegVariable* cast_var = func->newTempVariable(cmp_type);
                    str += "\t" + cast_var->getIRName() + " = zext " + s2_final_type->toString() + " " + s2_final_name + " to " + cmp_type_str + "\n";
                    s2_final_name = cast_var->getIRName();
                    s2_final_type = cmp_type;
                } else { /* TODO: other casts */ }
            }
            
            // std::string llvm_cmp_op_str;
            // if (op == IRInstOperator::IRINST_OP_LESS_EQUAL_I) llvm_cmp_op_str = "icmp sle";
            // else if (op == IRInstOperator::IRINST_OP_GREATER_I) llvm_cmp_op_str = "icmp sgt";
            // else if (op == IRInstOperator::IRINST_OP_GREATER_EQUAL_I) llvm_cmp_op_str = "icmp sge";
            // else if (op == IRInstOperator::IRINST_OP_LESS_I) llvm_cmp_op_str = "icmp slt";
            // else if (op == IRInstOperator::IRINST_OP_EQ_I) llvm_cmp_op_str = "icmp eq";
            // else if (op == IRInstOperator::IRINST_OP_NEQ_I) llvm_cmp_op_str = "icmp ne";

            // str += "\t" + getIRName() + " = " + llvm_cmp_op_str + " " + cmp_type_str + " " + s1_final_name + ", " + s2_final_name;
            // 关系运算
            if (s1_final_type->isFloatType()) {
                // 浮点数比较
                std::string llvm_cmp_op_str;
                if (op == IRInstOperator::IRINST_OP_LESS_I) llvm_cmp_op_str = "fcmp olt";
                else if (op == IRInstOperator::IRINST_OP_LESS_EQUAL_I) llvm_cmp_op_str = "fcmp ole";
                else if (op == IRInstOperator::IRINST_OP_GREATER_I) llvm_cmp_op_str = "fcmp ogt";
                else if (op == IRInstOperator::IRINST_OP_GREATER_EQUAL_I) llvm_cmp_op_str = "fcmp oge";
                else if (op == IRInstOperator::IRINST_OP_EQ_I) llvm_cmp_op_str = "fcmp oeq";
                else if (op == IRInstOperator::IRINST_OP_NEQ_I) llvm_cmp_op_str = "fcmp one";
                str += "\t" + getIRName() + " = " + llvm_cmp_op_str + " " + s1_final_type->toString() + " " + s1_final_name + ", " + s2_final_name;
            } else if (s1_final_type->isIntegerType()) {
                // 整数比较
                std::string llvm_cmp_op_str;
                if (op == IRInstOperator::IRINST_OP_LESS_I) llvm_cmp_op_str = "icmp slt";
                else if (op == IRInstOperator::IRINST_OP_LESS_EQUAL_I) llvm_cmp_op_str = "icmp sle";
                else if (op == IRInstOperator::IRINST_OP_GREATER_I) llvm_cmp_op_str = "icmp sgt";
                else if (op == IRInstOperator::IRINST_OP_GREATER_EQUAL_I) llvm_cmp_op_str = "icmp sge";
                else if (op == IRInstOperator::IRINST_OP_EQ_I) llvm_cmp_op_str = "icmp eq";
                else if (op == IRInstOperator::IRINST_OP_NEQ_I) llvm_cmp_op_str = "icmp ne";
                str += "\t" + getIRName() + " = " + llvm_cmp_op_str + " " + s1_final_type->toString() + " " + s1_final_name + ", " + s2_final_name;
            }
            else {
                // 其他操作
                Instruction::toString(str);
                if (!str.empty() && str.find('\n') == std::string::npos) {
                    str = "\t" + str;
                }
            }
            break;
        }

        case IRInstOperator::IRINST_OP_LAND_I:
        case IRInstOperator::IRINST_OP_LOR_I:
        {
            // Logical ops. Operands and result are i1.
            Type* logical_op_type = IntegerType::getTypeBool(); // i1
            std::string logical_op_type_str = logical_op_type->toString();

            if (s1_final_type != logical_op_type) {
                if (logical_op_type->isInt1Ty() && s1_final_type->isInt32Ty()) { // i32 to i1
                    RegVariable* cast_var = func->newTempVariable(logical_op_type);
                    str += "\t" + cast_var->getIRName() + " = icmp ne " + s1_final_type->toString() + " " + s1_final_name + ", 0\n";
                    s1_final_name = cast_var->getIRName();
                    s1_final_type = logical_op_type;
                } else { /* TODO: other casts */ }
            }
            if (s2_final_type != logical_op_type) {
                if (logical_op_type->isInt1Ty() && s2_final_type->isInt32Ty()) { // i32 to i1
                    RegVariable* cast_var = func->newTempVariable(logical_op_type);
                    str += "\t" + cast_var->getIRName() + " = icmp ne " + s2_final_type->toString() + " " + s2_final_name + ", 0\n";
                    s2_final_name = cast_var->getIRName();
                    s2_final_type = logical_op_type;
                } else { /* TODO: other casts */ }
            }

            std::string llvm_logical_op_str;
            if (op == IRInstOperator::IRINST_OP_LAND_I) llvm_logical_op_str = "and";
            else if (op == IRInstOperator::IRINST_OP_LOR_I) llvm_logical_op_str = "or";
            str += "\t" + getIRName() + " = " + llvm_logical_op_str + " " + logical_op_type_str + " " + s1_final_name + ", " + s2_final_name;
            break;
        }
        default:
            // For other unknown binary ops, or if base class has a general toString
            Instruction::toString(str); 
            // Prepend tab if it's an actual instruction line from base
            if(!str.empty() && str.find('\n') == std::string::npos) { // Simple check if it's a single line
                str = "\t" + str;
            }
            break;
    }
    // Remove trailing newline and tab if str ends with "\n\t" from load/cast operations
    // and the main operation was appended.
    // This is a bit tricky; the current structure appends to str.
    // A better way might be to build prefix_str and then combine.
    // For now, ensure instructions are tabbed. If str was empty and now has one line, it's tabbed.
    // If str had prefix lines (already tabbed and ending in \n), the new line is also tabbed.
}